package com.itchu.auth.service;

import com.itchu.auth.config.JwtProperties;
import com.itchu.auth.config.OnlineCounter;
import com.itchu.common.domain.Payload;
import com.itchu.common.exption.ExceptionEnum;
import com.itchu.common.exption.MusicException;
import com.itchu.common.utils.CookieUtils;
import com.itchu.common.utils.JwtUtils;
import com.itchu.system.domain.Authority;
import com.itchu.system.domain.SysRole;
import com.itchu.system.domain.SysUser;
import com.itchu.system.domain.User;
import com.itchu.system.mapper.AuthorityMapper;
import com.itchu.system.mapper.UserMapper;
import com.itchu.system.service.UserService;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class AuthService {

    @Autowired
    private UserService userService;
    @Autowired
    private JwtProperties prop;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private AuthorityMapper authorityMapper;
    @Autowired
    private OnlineCounter onlineCounter;

    /**
     * 认证业务逻辑
     * @param username
     * @param password
     * @param response
     */
    public void login(String username, String password, HttpServletResponse response) {
        // 验证账号密码是否正确
        User user = userService.login(username, password);
        if (user == null) {
            throw new MusicException(ExceptionEnum.MUSIC_INVALID_USERNAME_PASSWORD_ERROR);
        }

        // 给封装到载荷的用户对象赋值
        User userInfo = new User();
        userInfo.setId(user.getId());
        userInfo.setUsername(user.getUsername());
        createAndSendTokenToCookie(response,userInfo);

    }

    /**
     * 创建token并发送到浏览器的cookie中
     * @param response
     * @param userInfo
     */
    private void createAndSendTokenToCookie(HttpServletResponse response , Object userInfo) {
        String token = JwtUtils.generateTokenExpireInMinutes(userInfo, prop.getPrivateKey(), prop.getUser().getExpire());
        CookieUtils.newCookieBuilder()
                .response(response)
                .name(prop.getUser().getCookieName())
                .value(token)
                .domain(prop.getUser().getCookieDomain())
                .httpOnly(true)
                .build();

        System.out.println("登录成功了，写入cookie1");
    }

    /**
     * 认证业务
     * @param request
     * @param response
     * @return
     */
    public User verify(HttpServletRequest request, HttpServletResponse response) {

        // 1.获取存放在cookie的token
        String token = CookieUtils.getCookieValue(request, prop.getUser().getCookieName());
        if (StringUtils.isEmpty(token)) {
            throw new MusicException(ExceptionEnum.MUSIC_INVALID_UNAUTHRIZED);
        }

        // 2.解析token
        Payload<User> payload = null;
        try {
            payload = JwtUtils.getInfoFromToken(token, prop.getPublicKey(), User.class);
        } catch (Exception e) {
            throw new MusicException(ExceptionEnum.MUSIC_INVALID_UNAUTHRIZED);
        }

        // 判断tokenId是否在redis里面，是就返回已过期或未登录
        if (redisTemplate.hasKey(payload.getId())) {
            throw new MusicException(ExceptionEnum.MUSIC_INVALID_UNAUTHRIZED);
        }

        User userInfo = payload.getUserInfo();

        // 把用户的key和过期时间存进OnlineCounter的类里
        onlineCounter.initMap(userInfo.getId().toString(),payload.getExpiration());

        // 3. 获取过期时间
        Date exTime = payload.getExpiration();

        // 4.获取刷新的时间点(过期时间点 - 刷新时长)
        DateTime refreshTime = new DateTime(exTime).minusMinutes(prop.getUser().getRefreshTime());

        // 5.如果当前时间在刷新点时间的前面就创建新的token,这样降低服务器压力
        if (refreshTime.isBefore(System.currentTimeMillis())) {
            createAndSendTokenToCookie(response,userInfo);
        }

        User user = userMapper.selectByPrimaryKey(userInfo.getId());

        if (user == null) {
            throw new MusicException(ExceptionEnum.MUSIC_FAIL_AUTHOR);
        }
        return user;
    }

    /**
     * 退出登录
     * @param request
     * @param response
     * @return
     */
    public Boolean logout(HttpServletRequest request, HttpServletResponse response) {
        // 1.从cookie获取token
        String token = CookieUtils.getCookieValue(request, prop.getUser().getCookieName());
        // 2.验证token是否合法
        Payload<User> payload = null;
        try {
            payload = JwtUtils.getInfoFromToken(token, prop.getPublicKey(), User.class);
        } catch (Exception e) {
            throw new MusicException(ExceptionEnum.MUSIC_INVALID_UNAUTHRIZED);
        }

        // 3.判断过期时间是否大于10s,是就存就redis里面（作用是当作黑名单处理）
        Date exTime = payload.getExpiration();
        long remainTime = exTime.getTime() - System.currentTimeMillis();
        if (remainTime > 10000) {
            redisTemplate.opsForValue().set(payload.getId(),"1",remainTime, TimeUnit.MILLISECONDS);
        }
        // 4.删除cookie
        CookieUtils.deleteCookie(prop.getUser().getCookieName(),prop.getUser().getCookieDomain(),response);


        // 用户退出时，后台管理系统的在线·人数要减1
        onlineCounter.initMap(payload.getUserInfo().getId().toString(),new Date());
        return true;
    }


    /**
     * 从cookie中解析出载荷，并返回用户信息
     * @param request
     * @param response
     * @return
     */
    public Map<String, Object> getVerifyUser(HttpServletRequest request, HttpServletResponse response) {
        // 1.获取存放在cookie的token
        String token = CookieUtils.getCookieValue(request, prop.getUser().getAuthCookieName());
        if (StringUtils.isEmpty(token)) {
            throw new MusicException(ExceptionEnum.MUSIC_INVALID_UNAUTHRIZED);
        }
        // 2.解析token
        Payload<SysUser> payload = null;
        try {
            payload = JwtUtils.getInfoFromToken(token, prop.getPublicKey(), SysUser.class);
        } catch (Exception e) {
            throw new MusicException(ExceptionEnum.MUSIC_INVALID_UNAUTHRIZED);
        }

        // 判断tokenId是否在redis里面，是就返回已过期或未登录
        if (redisTemplate.hasKey(payload.getId())) {
            throw new MusicException(ExceptionEnum.MUSIC_INVALID_UNAUTHRIZED);
        }

        SysUser userInfo = payload.getUserInfo();

        Map<String ,Object> map = new HashMap<>();


        // 二、获取动态菜单
        List<Long> rids = userInfo.getRoles().stream().map(SysRole::getId).collect(Collectors.toList());

        List<Authority> authorityList = authorityMapper.getDynamicMenus(rids);
        List<Authority> parent = new ArrayList<>();
        authorityList.stream().forEach(authority -> {
            if (authority.getParentId().equals(0L)) {
                parent.add(authority);
            }
        });
        parent.stream().forEach(p -> {
            List<Authority> child = new ArrayList<>();
            authorityList.stream().forEach(authority -> {
                if (p.getId().equals(authority.getParentId())) {
                    child.add(authority);
                }
                p.setChildren(child);
            });
        });
        map.put("userInfo",userInfo);
        map.put("menus",parent);

        return map;
    }

}
